package com.ybear.ybutils.utils.webp

import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.view.View
import android.widget.ImageView
import androidx.annotation.DrawableRes
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.RequestManager
import com.bumptech.glide.integration.webp.decoder.WebpDrawable
import com.bumptech.glide.integration.webp.decoder.WebpDrawableTransformation
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.Transformation
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.CenterInside
import com.bumptech.glide.request.target.Target
import com.ybear.ybutils.utils.ResUtil
import com.ybear.ybutils.utils.SysUtil
import com.ybear.ybutils.utils.log.LogUtil
import java.io.File

/**
 * Webp加载管理器
 *
 */
open class WebpManager private constructor() {
    companion object {
        private const val TAG = "WebpManager"
        @JvmStatic
        fun with(context: Context) : Request { return Request( context ) }

        @JvmStatic
        fun with(view: View) : Request { return Request( view.context ) }
    }

    class Request(context: Context) {
        private var ctx : Context = context
        private var isDownload = false

        private fun build(obj: Any?) : Builder<*> {
            return if( isDownload )
                Builder<File>( ctx, obj, true )
            else
                Builder<Drawable>( ctx, obj, false )
        }

        /**
         * 设置一个字符串地址
         * - 可用范围：
         *      1.HTTP请求
         *      2.本地资源字符串，eg: ic_img.webp
         */
        fun load(url: String?) : Builder<*> {
            return build( url )
        }

        /**
         * 设置一个本地资源地址
         * - 可用范围：
         *      1.R.drawable.ic_img.webp
         */
        fun load(@DrawableRes resId: Int) : Builder<*> {
            return build( resId )
        }

        /**
         * 设置一个Drawable资源
         */
        fun load(drawable: Drawable?) : Builder<*> {
            return build( drawable )
        }

        /**
         * 设置后会下载当前webp
         */
        fun download() : Request {
            isDownload = true
            return this
        }
    }

    class Builder<T> (
        private var context: Context,
        private var obj: Any?,
        private var isDownload: Boolean) {

        private var width = 0
        private var height = 0
        private var loopCount = 0
        private var placeholder : Drawable? = null
        private var fallback : Drawable? = null
        private var error : Drawable? = null
        private var mRequestListener : RequestListener? = null

        /**
         * 设置Webp尺寸大小
         */
        fun override(width: Int, height: Int) : Builder<T> {
            this.width = width
            this.height = height
            return this
        }

        /**
         * 设置Webp尺寸大小
         */
        fun override(size: Int) : Builder<T> { return override( size, size ) }

        /**
         * 设置循环播放次数，当小于0时默认无限循环
         */
        fun loopCount(loopCount: Int) : Builder<T> {
            this.loopCount = loopCount
            return this
        }

        /**
         * 设置默认占位图
         */
        fun placeholder(drawable: Drawable?) : Builder<T> {
            placeholder = drawable
            return this
        }

        /**
         * 设置默认占位图
         */
        fun placeholder(resId: Int) : Builder<T> {
            placeholder = ResUtil.getDrawable( context, resId )
            return this
        }

        /**
         * 设置空请求占位图
         */
        fun fallback(drawable: Drawable?) : Builder<T> {
            fallback = drawable
            return this
        }

        /**
         * 设置空请求占位图
         */
        fun fallback(resId: Int) : Builder<T> {
            fallback = ResUtil.getDrawable( context, resId )
            return this
        }

        /**
         * 设置加载失败占位图
         */
        fun error(drawable: Drawable?) : Builder<T> {
            error = drawable
            return this
        }

        /**
         * 设置加载失败占位图
         */
        fun error(resId: Int) : Builder<T> {
            error = ResUtil.getDrawable( context, resId )
            return this
        }

        /**
         * 设置全部占位图均为同一资源
         */
        fun defaults(drawable: Drawable?) : Builder<T> {
            placeholder( drawable )
            fallback( drawable )
            return error( drawable )
        }

        /**
         * 设置全部占位图均为同一资源
         */
        fun defaults(resId: Int) : Builder<T> {
            placeholder( resId )
            fallback( resId )
            return error( resId )
        }

        fun listener(listener: RequestListener) : Builder<T> {
            mRequestListener = listener
            return this
        }

        /**
         * 设置展示ImageView控件的请求
         */
        fun into(view: ImageView) { request()?.into( view ) }

        /**
         * 设置无ImageView控件的请求（一般为下载时会用到）
         */
        fun submit() { request()?.submit() }

        private fun request() : RequestBuilder<*>? {
            if( obj == null ) {
                LogUtil.e( TAG, "Webp.Builder.request -> obj is null." )
                return null
            }
            val rm: RequestManager? = if ( SysUtil.checkContextValid( context ) )
                Glide.with( context )
            else
                null
            if( rm == null ) {
                LogUtil.e( TAG, "Webp.Builder.request -> RequestManager is null." )
                return null
            }
            //下载模式时，跳过其他配置
            if( isDownload ) {
                /* 加载编译 */
                return loadBuild( rm.downloadOnly() )
                    /* 加载监听器 */
                    ?.addListener( object : com.bumptech.glide.request.RequestListener<File> {
                        override fun onLoadFailed(e: GlideException?,
                                                  model: Any?,
                                                  target: Target<File>,
                                                  isFirstResource: Boolean): Boolean {
                            return mRequestListener?.onError( e, model, isFirstResource ) == true
                        }
                        override fun onResourceReady(res: File,
                                                     model: Any,
                                                     target: Target<File>?,
                                                     dataSource: DataSource,
                                                     isFirstResource: Boolean): Boolean {
                            return mRequestListener
                                ?.onReadyDownload( res, model, isFirstResource ) == true
                        }
                })
            }

            val tf: Transformation<Bitmap> = CenterInside()
            /* 加载编译 */
            return loadBuild( rm.asDrawable() )
                /* Webp核心 */
                ?.optionalTransform( tf )
                ?.optionalTransform(
                    WebpDrawable::class.java,
                    WebpDrawableTransformation( tf )
                )
                /* 加载监听器 */
                ?.addListener( object : com.bumptech.glide.request.RequestListener<Drawable> {
                    override fun onLoadFailed(e: GlideException?,
                                              model: Any?,
                                              target: Target<Drawable>,
                                              isFirstResource: Boolean): Boolean {
                        return mRequestListener?.onError( e, model, isFirstResource ) == true
                    }

                    override fun onResourceReady(res: Drawable,
                                                 model: Any,
                                                 target: Target<Drawable>?,
                                                 dataSource: DataSource,
                                                 isFirstResource: Boolean): Boolean {
                        //循环次数，0表示无限循环
                        if ( loopCount > 0 && res is WebpDrawable ) res.loopCount = loopCount
                        return mRequestListener?.onReady( res, model, isFirstResource ) == true
                    }
                })
        }

        private fun <T> loadString(rb : RequestBuilder<T>): RequestBuilder<T>? {
            val url = obj?.toString() ?: return null
            if( TextUtils.isEmpty( url ) ) return null
            val isHttp: Boolean = url.startsWith( "http" )
            val isWebp: Boolean = url.endsWith( "webp" )
            LogUtil.d( TAG, "loadWebp -> isHttp:$isHttp, isWebp:$isWebp, url:$url" )
            //链接格式
            if ( isHttp ) {
                return rb.load( url )
            }
            //本地资源格式
            if( isWebp ) {
                return loadLocalRes( rb )
            }
            return null
        }

        private fun <T> loadLocalRes(rb : RequestBuilder<T>): RequestBuilder<T>? {
            val url = obj?.toString() ?: return null
            val res = ResUtil.getDrawableId( context, url )
            if( res <= 0 ) return null
            return rb.load( res )
        }

        private fun <T> loadDrawable(rb : RequestBuilder<T>): RequestBuilder<T>? {
            return if( obj is Drawable ) rb.load( obj as Drawable ) else null
        }

        private fun <T> loadBuild(rb : RequestBuilder<T>): RequestBuilder<T>? {
            var rbBuild = rb
            if( width > 0 && height > 0 ) rbBuild = rb.override( width, height )
            /* 默认展示图 */
            placeholder?.apply { rbBuild = rb.placeholder( this ) }
            fallback?.apply { rbBuild = rb.fallback( this ) }
            error?.apply { rbBuild = rb.error( this ) }
            return when ( obj ) {
                /* 字符串类型，http/resource 均有可能 */
                is String -> loadString( rbBuild )
                /* 本地资源类型 */
                is Int ->  loadLocalRes( rbBuild )
                /* 默认Drawable */
                is Drawable -> loadDrawable( rbBuild )
                else -> null
            }
        }
    }

    interface RequestListener {
        fun onReady(resource: Drawable?, model: Any?, isFirstResource: Boolean): Boolean
        fun onReadyDownload(resource: File, model: Any?, isFirstResource: Boolean): Boolean
        fun onError(e: GlideException?, model: Any?, isFirstResource: Boolean): Boolean
    }
}